package com.cloud.bssp.gateway.filter;

import com.alibaba.fastjson.JSONObject;
import com.cloud.bssp.gateway.config.redis.RedisUtil;
import com.cloud.bssp.gateway.enums.CommonReturnEnum;
import com.cloud.bssp.gateway.util.ErrorResult;
import com.cloud.bssp.gateway.util.JwtUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.PathContainer;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.nio.charset.StandardCharsets;
import java.util.Date;
import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * description: 权限过滤器
 *
 * @author: weirx
 * @time: 2021/3/26 11:05
 */
@Component
public class AuthFilter implements GlobalFilter {

    @Value("#{'${gateway.filter.white-list}'.split(',')}")
    private List<String> whiteList;

    @Autowired
    private RedisUtil redisUtil;

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        ServerHttpRequest request = exchange.getRequest();
        HttpHeaders header = request.getHeaders();
        String token = header.getFirst(JwtUtil.HEADER_AUTH);
        String username = header.getFirst(JwtUtil.HEADER_USERNAME);
        PathContainer pathContainer = request.getPath().pathWithinApplication();
        String path = pathContainer.value();

        //白名单，放行
        if (whiteList.contains(path)) {
            return chain.filter(exchange);
        }
        //token为空
        if (StringUtils.isEmpty(token)) {
            return this.sendError(path, exchange.getResponse(), CommonReturnEnum.UNAUTHORIZED.getCode(),
                    CommonReturnEnum.UNAUTHORIZED.getName());
        }
        if (StringUtils.isEmpty(username)) {
            return this.sendError(path, exchange.getResponse(), CommonReturnEnum.USERNAME_IS_NULL.getCode(),
                    CommonReturnEnum.USERNAME_IS_NULL.getName());
        }
        String tokenCache = redisUtil.getObject(JwtUtil.TOKEN_CACHE_PREFIX + username);
        //token过期
        if (StringUtils.isEmpty(tokenCache)) {
            return this.sendError(path, exchange.getResponse(), CommonReturnEnum.TOKEN_TIMEOUT.getCode(),
                    CommonReturnEnum.TOKEN_TIMEOUT.getName());
        }
        //没过期，对比当前token和缓存的token是否一致
        if (!token.equals(tokenCache)) {
            return this.sendError(path, exchange.getResponse(), CommonReturnEnum.UNAUTHORIZED.getCode(),
                    CommonReturnEnum.UNAUTHORIZED.getName());
        }
        //token续期
        redisUtil.setObjectExpire(JwtUtil.TOKEN_CACHE_PREFIX + username, token,
                JwtUtil.TOKEN_REDIS_EXPIRE, TimeUnit.MINUTES);

        //将用户信息存放在请求header中传递给下游业务
        ServerHttpRequest.Builder mutate = request.mutate();
        mutate.header(JwtUtil.HEADER_USERNAME, username);
        ServerHttpRequest buildRequest = mutate.build();

        return chain.filter(exchange.mutate().request(buildRequest).build());
    }

    protected Mono<Void> sendError(String url, ServerHttpResponse httpResponse, int code, String msg) {
        httpResponse.setStatusCode(HttpStatus.OK);
        httpResponse.getHeaders().add("Content-Type", "application/json;charset=UTF-8");
        ErrorResult result = new ErrorResult();
        result.setCode(code);
        result.setMessage(msg);
        result.setException(msg);
        result.setPath(url);
        result.setStatus(code);
        result.setSuccess(false);
        result.setTimestamp(new Date());
        byte[] responseByte = JSONObject.toJSONString(result).getBytes(StandardCharsets.UTF_8);
        DataBuffer buffer = httpResponse.bufferFactory().wrap(responseByte);
        return httpResponse.writeWith(Flux.just(buffer));
    }
}